package org.cardoso.coyote.persistence;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.cardoso.coyote.persistence.filter.OperatorType;
import org.cardoso.coyote.persistence.filter.QueryFilter;

/**
 * 
 * @author Jose Henrique Cardoso
 *
 */
public abstract class AbstractPersistence<T extends EntityBean> implements Serializable, Persistence<T> {

	private static final long serialVersionUID = 1L;

	protected EntityManager entityManager;
	protected Class<T> entityClass;
	
	public AbstractPersistence() {
		super();
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public AbstractPersistence(Class entityClass) {
		super();
		this.entityClass = entityClass;
	}
	
	@Override
	public T getEntity(Long id) {
		return (T) this.entityManager.find(this.entityClass, id);
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public T getEntity(Class entityClass, Long id) {
		return (T) this.entityManager.find(entityClass, id);
	}

	@Override
	public Long count() {
		return count(null);
	}

	@Override
	public Collection<T> find() {
		return find(null);
	}

	@Override
	public Long count(QueryFilter queryFilter) {

		try {

			queryFilter.removeFiltersInvalid();
			
			StringBuilder hql = new StringBuilder(QueryFilter.SELECT_COUNT.replaceAll("class", entityClass.getName()));

			buildQuery(queryFilter, hql);
			
			Query query = entityManager.createQuery(hql.toString());

			setParameters(queryFilter, query);

			return (Long) query.getSingleResult();
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return null;
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> find(QueryFilter queryFilter) {

		try {

			queryFilter.removeFiltersInvalid();

			StringBuilder hql = new StringBuilder(QueryFilter.SELECT.replaceAll("class", entityClass.getName()));
			
			buildQuery(queryFilter, hql);
			
			Query query = entityManager.createQuery(hql.toString());

			setParameters(queryFilter, query);

			if (queryFilter != null && 
					((queryFilter.getFirstResult() != null && queryFilter.getFirstResult() != QueryFilter.NO_PAGINATOR) && 
					 (queryFilter.getFirstResult() != null && queryFilter.getMaxResults() != QueryFilter.NO_PAGINATOR))) {
				query.setFirstResult(queryFilter.getFirstResult());
				query.setMaxResults(queryFilter.getMaxResults());
			}

			return query.getResultList();

		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;
	}

	private static void setParameters(QueryFilter queryFilter, Query query) {
		
		if (queryFilter.getFilters() != null && !queryFilter.getFilters().isEmpty()) {

			int sizeListfilters = queryFilter.getFilters().size();
			
			for (int indexFilter = 0; indexFilter < sizeListfilters; indexFilter++) {

				if (!queryFilter.getFilters().get(indexFilter).getOperatorType().equals(OperatorType.OPEN)
						&& !queryFilter.getFilters().get(indexFilter).getOperatorType().equals(OperatorType.CLOSE)
						&& !queryFilter.getFilters().get(indexFilter).getOperatorType().equals(OperatorType.OR)
						&& !queryFilter.getFilters().get(indexFilter).getOperatorType().equals(OperatorType.IS_NULL)
						&& !queryFilter.getFilters().get(indexFilter).getOperatorType().equals(OperatorType.IS_NOT_NULL)) {

					if (queryFilter.getFilters().get(indexFilter).getOperatorType().equals(OperatorType.LIKE))
						query.setParameter("parameter" + indexFilter, "%" + queryFilter.getFilters().get(indexFilter).getValue() + "%");
					else
						query.setParameter("parameter" + indexFilter, queryFilter.getFilters().get(indexFilter).getValue());
				}
			}
		}
	}

	private static void buildQuery(QueryFilter queryFilter, StringBuilder hql) {
		
		if (queryFilter.getFilters() != null && !queryFilter.getFilters().isEmpty()) {

			hql.append(QueryFilter.WHERE);

			int sizeListFilters = queryFilter.getFilters().size();

			for (int indexFilter = 0; indexFilter < sizeListFilters; indexFilter++) {

				if (queryFilter.getFilters().get(indexFilter).getOperatorType().equals(OperatorType.OPEN)) {
					hql.append(" ( ");
				} else if (queryFilter.getFilters().get(indexFilter).getOperatorType().equals(OperatorType.CLOSE)) {
					hql.append(" ) ");
				} else if (queryFilter.getFilters().get(indexFilter).getName().equals(OperatorType.OR)) {
					hql.append(QueryFilter.OR);
				} else if (queryFilter.getFilters().get(indexFilter).getOperatorType().equals(OperatorType.IS_NOT_NULL)
						|| queryFilter.getFilters().get(indexFilter).getOperatorType().equals(OperatorType.IS_NULL)) {
					hql.append(queryFilter.getFilters().get(indexFilter));
				} else if (queryFilter.getFilters().get(indexFilter).getOperatorType().equals(OperatorType.DIFFERENT)
						|| queryFilter.getFilters().get(indexFilter).getOperatorType().equals(OperatorType.GREATER)
						|| queryFilter.getFilters().get(indexFilter).getOperatorType().equals(OperatorType.LESS)
						|| queryFilter.getFilters().get(indexFilter).getOperatorType().equals(OperatorType.GREATER_OR_EQUAL)
						|| queryFilter.getFilters().get(indexFilter).getOperatorType().equals(OperatorType.LESS_OR_EQUAL)) {
					hql.append(queryFilter.getFilters().get(indexFilter).getName() + " :parameter" + indexFilter);
				} else if (queryFilter.getFilters().get(indexFilter).getOperatorType().equals(OperatorType.LIKE)) {
					hql.append("upper(" + queryFilter.getFilters().get(indexFilter).getName() + ") like" + " upper(:parameter" + indexFilter + ")");
				} else {
					hql.append(queryFilter.getFilters().get(indexFilter).getName() + " = :parameter" + indexFilter);
				}

				if (indexFilter < (sizeListFilters - 1)) {
					if (!queryFilter.getFilters().get(indexFilter).getOperatorType().equals(OperatorType.OPEN)
							&& !queryFilter.getFilters().get(indexFilter + 1).getOperatorType().equals(OperatorType.CLOSE)
							&& !queryFilter.getFilters().get(indexFilter).getOperatorType().equals(OperatorType.OR)
							&& !queryFilter.getFilters().get(indexFilter + 1).getOperatorType().equals(OperatorType.OR)) {
						hql.append(" and ");
					}
				}
			}
		}

		if (queryFilter != null && (queryFilter.getOrderBy() != null && !queryFilter.getOrderBy().trim().equals(""))) {
			hql.append(" " + QueryFilter.ORDER_BY.replaceAll("orderBy", queryFilter.getOrderBy()));
		}
	}
	
}